package com.greate.community.auth.wechatauth.wechat;

import com.greate.community.auth.wechatauth.domain.UserWeChat;
import com.greate.community.auth.wechatauth.sso.ClientResources;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.token.RequestEnhancer;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
 * 微信登录流程
 *
 * @author xzx
 * @date 2022/1/29 18:40
 */
@Configuration
@EnableOAuth2Client
@Order(200)
public class WeChatAuthorizationConfig {

    // 微信认证登录的URL
    private static final String WE_CHAT_LOGIN_URL = "/wechat/oauth2/login";
    // 认证成功后跳转的URL
    private static final String WE_CHAT_AUTHENTICATION_SUCCESS_URL = "/wechat/login";

    private final OAuth2ClientContext context;
    private final RequestEnhancer requestEnhancer;
    private final PrincipalExtractor principalExtractor;
    private final ClientResources clientResources;

    public WeChatAuthorizationConfig(
            @Qualifier("oauth2ClientContext") OAuth2ClientContext context,
            @Qualifier("weChatRequestEnhancer") RequestEnhancer requestEnhancer,
            @Qualifier("weChatUserPrincipalExtractor") PrincipalExtractor principalExtractor,
            @Qualifier("weChat") ClientResources clientResources
    ) {
        this.context = context;
        this.requestEnhancer = requestEnhancer;
        this.principalExtractor = principalExtractor;
        this.clientResources = clientResources;
    }

    private static List<HttpMessageConverter<?>> convertMediaTypes() {
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        messageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
        return Collections.singletonList(messageConverter);
    }

    // 通过使用授权码来获取oauth2访问令牌的提供者
    private AuthorizationCodeAccessTokenProvider accessTokenProvider() {
        AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
        provider.setAuthorizationRequestEnhancer(requestEnhancer);
        provider.setTokenRequestEnhancer(requestEnhancer);
        provider.setMessageConverters(WeChatAuthorizationConfig.convertMediaTypes());
        return provider;
    }

    private WeChatRestTemplate restTemplate() {
        // 获取微信客户端配置信息
        WeChatRestTemplate template = new WeChatRestTemplate(clientResources.getClient(), context);
        template.setAccessTokenProvider(accessTokenProvider());
        template.setMessageConverters(WeChatAuthorizationConfig.convertMediaTypes());
        return template;
    }

    /**
     * 返回 OAuth2 认证过滤器，将该过滤器注入到过滤器链中
     *
     * @return
     */
    public OAuth2ClientAuthenticationProcessingFilter weChatFilter() {
        OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(WE_CHAT_LOGIN_URL);


        filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler() {
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

                UserWeChat userWeChat = (UserWeChat) authentication.getPrincipal();
                String openid = userWeChat.getOpenId();
                String headImgUrl = userWeChat.getHeadimgurl();

                this.setDefaultTargetUrl(WE_CHAT_AUTHENTICATION_SUCCESS_URL + "?openid=" + openid + "&headImgUrl=" + headImgUrl);
                super.onAuthenticationSuccess(request, response, authentication);
            }
        });

        WeChatRestTemplate template = restTemplate();
        filter.setRestTemplate(template);
        UserInfoTokenServices tokenServices = tokenServices(template);
        filter.setTokenServices(tokenServices);
        return filter;
    }

    private UserInfoTokenServices tokenServices(WeChatRestTemplate template) {
        UserInfoTokenServices tokenServices = new UserInfoTokenServices(
                clientResources.getResource().getUserInfoUri(), clientResources.getResource().getClientId());
        tokenServices.setRestTemplate(template);
        tokenServices.setPrincipalExtractor(principalExtractor);
        return tokenServices;
    }
}
